# -*- coding: utf-8 -*-
"""
conftest.py - WMS Q4 项目配置
"""
import os
import pytest
import pytest_asyncio
from pathlib import Path
from business.web_ui.wm4_q4.login_business.loginbusiness import LoginBusiness
from playwright.async_api import async_playwright
from tools.general_tool import ReadConf
from tools.log import Logger
from datetime import datetime
from tools.db_connection import DbConnectionFactory
from tools.path_manage import ProjectFilePath

logger = Logger().logger

# 登录态文件路径
root_dir = Path(__file__).resolve().parents[3]
auth_dir = root_dir / 'auth'
auth_dir.mkdir(parents=True, exist_ok=True)
STORAGE_STATE = str(auth_dir / 'storage_state.json')


@pytest_asyncio.fixture(scope="function")
async def browser_context():
    """浏览器上下文 fixture"""
    p = await async_playwright().start()

    browser = await p.chromium.launch(
        headless=True,
        # headless=False,
        args=[
            '--no-sandbox',
            '--disable-setuid-sandbox',
            '--disable-blink-features=AutomationControlled',
            '--disable-dev-shm-usage',
        ],
    )

    # 创建上下文配置
    context_options = {
        'viewport': {'width': 1440, 'height': 1080},
        'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/120.0.0.0 Safari/537.36',
    }

    # 如果存在登录态文件,尝试加载它
    if os.path.exists(STORAGE_STATE):
        try:
            # 验证文件是否有效（检查文件大小和格式）
            if os.path.getsize(STORAGE_STATE) > 0:
                with open(STORAGE_STATE, 'r') as f:
                    import json
                    json.load(f)  # 验证是否为有效JSON
                context_options['storage_state'] = STORAGE_STATE
                logger.info(f"已加载保存的登录状态: {STORAGE_STATE}")
            else:
                logger.warning("登录态文件为空，将删除并重新登录")
                os.remove(STORAGE_STATE)
                logger.info("未找到有效登录态文件,将执行首次登录")
        except (json.JSONDecodeError, OSError) as e:
            logger.warning(f"登录态文件损坏: {e}，将删除并重新登录")
            try:
                os.remove(STORAGE_STATE)
            except:
                pass
            logger.info("未找到有效登录态文件,将执行首次登录")
    else:
        logger.info("未找到登录态文件,将执行首次登录")

    context = await browser.new_context(**context_options)

    yield context
    await context.close()
    await browser.close()
    await p.stop()


@pytest_asyncio.fixture(scope="function")
async def logged_in_page(browser_context):
    """已登录的页面 fixture"""
    page = await browser_context.new_page()

    # 隐藏 webdriver 特征
    await page.add_init_script(
        """
        Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
        """
    )
    page.set_default_timeout(60000)

    # 创建登录业务对象
    login_business = LoginBusiness(page)
    
    # 检查是否需要登录
    need_login = False
    
    if not os.path.exists(STORAGE_STATE):
        need_login = True
        logger.info("执行首次登录...")
    else:
        # 验证登录态文件是否有效
        try:
            if os.path.getsize(STORAGE_STATE) == 0:
                need_login = True
                logger.info("登录态文件为空，重新登录...")
        except OSError:
            need_login = True
            logger.info("登录态文件访问异常，重新登录...")
    
    if need_login:
        try:
            await login_business.login()  # 使用配置文件中的凭据
            # 保存登录态
            await browser_context.storage_state(path=STORAGE_STATE)
            logger.info(f"✅ 登录完成并保存登录状态: {STORAGE_STATE}")
        except Exception as e:
            logger.error(f"❌ 登录失败: {e}")
            # 确保创建一个空的有效JSON文件，避免后续读取错误
            with open(STORAGE_STATE, 'w') as f:
                import json
                json.dump({}, f)
            raise
    else:
        # 访问首页激活登录态
        logger.info("使用已保存的登录状态,访问首页激活...")
        # 根据环境配置获取首页URL
        login_business = LoginBusiness(page)
        base_url = login_business._get_login_url().replace('/login', '')
        await page.goto(base_url, wait_until="networkidle", timeout=30000)
        
        # 等待页面完全加载
        await page.wait_for_timeout(2000)
        
        # 验证是否真的登录成功
        current_url = page.url
        logger.info(f"当前页面URL: {current_url}")
        
        # 如果在登录页,说明登录态失效,需要重新登录
        if "login" in current_url:
            logger.warning("⚠️ 登录态已失效,重新登录...")
            os.remove(STORAGE_STATE)  # 删除失效的登录态文件
            await login_business.login()  # 使用配置文件中的凭据
            await browser_context.storage_state(path=STORAGE_STATE)
            logger.info(f"✅ 重新登录完成并保存登录状态")
        else:
            logger.info("✅ 登录态已激活")

    # 切换仓库
    conf_path = Path(__file__).resolve().parent.parent.parent.parent / 'conf' / 'login_conf.ini'
    readconf = ReadConf(conf_path)
    warehouse_info = readconf.get_conf('WAREHOUSE')
    from_warehouse = warehouse_info.get('from_warehouse')
    to_warehouse = warehouse_info.get('to_warehouse')
    
    # 确保仓库信息不为空
    if from_warehouse and to_warehouse:
        await login_business.switch_warehouse(from_warehouse, to_warehouse)
    else:
        logger.warning("仓库信息配置不完整，跳过仓库切换")

    yield page
    await page.close()


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """
    pytest钩子：每个测试用例执行完成后自动调用
    """
    outcome = yield
    rep = outcome.get_result()
    page = item.funcargs.get("logged_in_page")  # 修改：使用Playwright的page对象
    current_date = datetime.now().strftime('%Y-%m-%d')

    if rep.when == 'call':  # 只在测试调用阶段处理
        # 1. 获取用例元数据
        keywords = item.keywords
        allure_label = keywords.get('allure_label')
        feature = getattr(allure_label, 'args', [''])[0]  # 功能模块
        title = keywords.get('__allure_display_name__', item.nodeid)  # 用例标题
        
        # 从测试用例的参数中获取"场景"字段
        case_data = item.funcargs.get('case', {})  # 获取参数化的case数据
        scenario = case_data.get('场景', item.nodeid)  # 优先使用"场景"字段,否则使用node_id
        logger.info(f"📋 测试场景: {scenario}")

        # 2. 初始化数据库连接
        with DbConnectionFactory() as db_factory:
            project_file_path = ProjectFilePath()
            # 通过getattr安全访问属性
            test_db_path = getattr(project_file_path, 'test_result_db_path', None)
            if test_db_path:
                sqlite_conn = db_factory.create_connection('sqlite', db_path=test_db_path).connect()
            else:
                # 如果无法获取数据库路径，使用默认路径
                from tools.path_manage import ROOT_PATH
                test_db_path = str(ROOT_PATH / 'auto_test_result_db.sqlite')
                sqlite_conn = db_factory.create_connection('sqlite', db_path=test_db_path).connect()

            # 3. 根据测试结果准备不同数据
            if rep.passed:
                # ✅ 测试通过
                execution_result = 'pass'
                extra_fields = ''
                extra_values = ''
                fail_params = []

            elif rep.failed:
                # ❌ 测试失败 - 自动截图
                execution_result = 'fail'
                
                # 修改：从 Allure 报告中获取已有的截图，或者跳过截图
                # Playwright 的失败截图已由 pytest-playwright 插件自动处理
                extra_fields = ''
                extra_values = ''
                fail_params = []
                logger.warning("⚠️ 测试失败，截图已由 Allure 自动处理")

            elif rep.skipped:
                # ⏭️ 测试跳过
                execution_result = 'skip'
                extra_fields = ''
                extra_values = ''
                fail_params = []
            else:
                return

            # 4. 检查是否已存在记录（同一天同一场景）
            test_db_id = sqlite_conn.select(
                table='test_result_tb',
                fields=["id"],
                where={"scenario": scenario, "create_date": current_date}  # 使用"场景"字段
            )

            if test_db_id and isinstance(test_db_id, list) and len(test_db_id) > 0:
                # 更新已有记录
                sqlite_conn.update(
                    table='test_result_tb',
                    updates={"execution_result": execution_result},
                    where={"id": test_db_id[0][0], "create_date": current_date}
                )
                logger.info(f"✅ 更新测试结果: {scenario} -> {execution_result}")
            else:
                # 插入新记录（动态SQL支持失败时的额外字段）
                base_sql = '''INSERT INTO test_result_tb
                              (execution_result, title, feature, scenario, create_date{extra_fields})
                              VALUES (?, ?, ?, ?, ?{extra_values})'''

                insert_params = [execution_result, title, feature, scenario, current_date] + fail_params  # 使用"场景"字段

                sqlite_conn.execute(
                    base_sql.format(extra_fields=extra_fields, extra_values=extra_values),
                    insert_params
                )
                logger.info(f"✅ 插入测试结果: {scenario} -> {execution_result}")